import { rgbaToId } from '../utils/helper'
import EventSimulator, { ActionType } from '../utils/EventSimulator'
import { Shape } from 'src/types/shape'
import ShapeEvent from 'src/plugin/ShapeEvent'
import Group from './Group'
import { shapeType } from 'src/types/drawBoard'

export type Option = {
  width: number
  height: number
}

// 场景
export default class {
  private canvas: HTMLCanvasElement
  private osCanvas: OffscreenCanvas
  ctx: CanvasRenderingContext2D | null
  osCtx: OffscreenCanvasRenderingContext2D | null
  dpr: number

  width: number = 300
  height: number = 250

  eventSimulator: EventSimulator //事件总线
  IDS: Set<string> //所有元素id集合
  historyID: string[] = []
  shapes: Array<Shape> //形状集合
  group: Array<Group> = []
  shapesEvent: ShapeEvent
  emptySpace: boolean = false //点击区域是否为空白区域
  activeShape?: Shape | null //当前选中形状

  constructor(canvas: HTMLCanvasElement, option?: Option) {
    if (!canvas || !(canvas instanceof Element)) throw new Error('The incoming type is incorrect, please pass in the "Element" type')
    // const dpr = window.devicePixelRatio;
    const dpr = 1;

    if (option) {
      this.width = option.width || 300
      this.height = option.height || 250
    }

    this.canvas = canvas
    // this.canvas = document.createElement('canvas')
    this.osCanvas = new OffscreenCanvas(this.canvas.width, this.canvas.height)
    // this.osCanvas = document.createElement('canvas')

    this.canvas.width = this.width
    this.canvas.height = this.height
    this.osCanvas.width = this.width
    this.osCanvas.height = this.height
    // this.osCanvas.id = 'bagua'

    this.ctx = this.canvas.getContext('2d')
    // @ts-ignore
    this.osCtx = this.osCanvas.getContext('2d');

    this.ctx?.scale(dpr, dpr);
    this.osCtx?.scale(dpr, dpr);
    this.dpr = dpr;

    // 事件监听
    this.canvas.addEventListener('mousedown', this.handleCreator(ActionType.Down))
    this.canvas.addEventListener('mouseup', this.handleCreator(ActionType.Up))
    this.canvas.addEventListener('mousemove', this.handleCreator(ActionType.Move))

    // 事件模型（事件车）
    this.eventSimulator = new EventSimulator()
    this.shapes = new Array()
    this.IDS = new Set()

    this.shapesEvent = new ShapeEvent(this)
    this.shapesEvent.open()
    // document.querySelector('#app')?.appendChild(this.osCanvas)
  }
  // 处理事件触发、派发事件
  handleCreator = (type: ActionType) => (evt: MouseEvent) => {
    const x = evt.offsetX;
    const y = evt.offsetY;
    const id = this.hitJudge(x, y);
    this.eventSimulator.addAction({ type, id }, evt);

    // 空白区域
    if (type == ActionType.Down && this.osCtx) {
      const rgba = Array.from(this.osCtx.getImageData(x * this.dpr, y * this.dpr, 1, 1).data);
      this.emptySpace = rgba.every(v => v == 0)
    }
    this.shapesEvent.handleFrameBox(type, id, evt)
  }
  // 查找对应ID
  hitJudge(x: number, y: number): string {
    if (!this.osCtx) return ''
    const rgba = Array.from(this.osCtx.getImageData(x * this.dpr, y * this.dpr, 1, 1).data);
    const id = rgbaToId(rgba as [number, number, number, number]);
    return this.IDS.has(id) ? id : '';
  }
  // 添加形状
  add(shape: Shape) {
    this.shapes.push(shape)
    this.eventSimulator.addListeners(shape.getRgbaId(), shape.getListeners())
    this.IDS.add(shape.getRgbaId())
    this.historyID.push(shape.getRgbaId())
    if (shape.type == shapeType.group) {
      this.group.push(shape as Group)
    }
  }
  // 删除形状
  remove(id: string) {
    const index = this.shapes.findIndex(shape => shape.getRgbaId() == id)
    if (index == -1) return

    this.shapes.splice(index, 1)
    this.IDS.has(id) && this.IDS.delete(id)

    const index1 = this.group.findIndex(shape => shape.getRgbaId() == id)
    if (index1 != -1) this.group.splice(index1, 1)
  }
  // 渲染
  render() {
    if (!this.ctx || !this.osCtx) throw new Error('渲染失败')

    this.clearCanvas()

    for (let i = 0; i < this.shapes.length; i++) {
      if (this.historyID.indexOf(this.shapes[i].getRgbaId()) != -1) {
        this.shapes[i].draw(this.ctx, this.osCtx)
      }
    }
  }
  // 清除画布
  clearCanvas() {
    this.ctx!.clearRect(0, 0, this.width, this.height)
    this.osCtx!.clearRect(0, 0, this.width, this.height)
  }
  updateCanvasInfo() {
    this.canvas.width = this.width
    this.canvas.height = this.height
    this.osCanvas.width = this.width
    this.osCanvas.height = this.height
  }
  // canvas-dom对象
  get domElement() {
    return this.canvas
  }

}